/**
 * mgcars.c
 *
 *  Created on: 21 Nov 2011
 *      Author: marco
 */

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#include "cars.h"
#include "cars_messaging.h"
#include "comsock.h"
#include "dgraph.h"
#include "match.h"
#include "mgcars.h"
#include "queue.h"
#include "shortestpath.h"
#include "worker.h"

FILE *logfile;
graph_t *map;

int sock_fd;
struct sockaddr_un sock_a;


user_list_node *user_list;

message_t reply_msg;
message_t ok_msg;
message_t pwd_msg;
message_t already_msg;

pthread_mutex_t users_mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t offers_mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t requests_mtx = PTHREAD_MUTEX_INITIALIZER;

pthread_t match_pt;

queue *offers;
queue *requests;

volatile sig_atomic_t pending_signal = 0;

int main(int argc, char **argv) {
	user_list_node *uln;

	/*Controllo parametri di avvio*/
	if (argc != 3) {
		printf("Errore: fornire parametri\n"); //stderr?
		exit(-1);
	}
	printf("dopo primo contorllo\n");
	if (init(argv[1], argv[2]) == -1) {
		perror("Errore in fase di inizializzazione, server in chiusura!");
		exit(-1);
	}
	printf("dopo SECONDO contorllo\n");
	serve();
	printf("SERVER: fuori da serve\n");

	/*Server in uscita*/
	//attesa thread
	uln = user_list;

	while(uln != NULL){
		if(uln->user->sfd != 0){
			printf("In attesa del worker di %s\n",uln->user->name);
			pthread_join(*(uln->user->tid),NULL);
		}
		uln = uln->next;
	}

	pthread_join(match_pt,NULL);
	printf("JOIN MATCH!!!\n");

	if (unlink(SOCKPATH) != 0) {
		perror("SERVER: UNLINK FINALE");
	}
	exit(EXIT_SUCCESS);
}

/**
 * Funzione di inizializzazione del server, si occupa della creazione del socket e del caricamento del grafo
 * @param cities_filename
 * @param roads_filename
 * @return
 */

int init(char *cities_filename, char *roads_filename) {

	FILE *fdnodes = NULL;
	FILE *fdarcs = NULL;

	/*Inizializzazione dei messaggi di risposta alle richieste di login*/
	ok_msg.buffer = NULL;
	ok_msg.length = 0;
	ok_msg.type = MSG_OK;

	pwd_msg.buffer = PWD_ERR;
	pwd_msg.length = strlen(PWD_ERR);
	pwd_msg.type = MSG_NO;

	already_msg.buffer = ALREADY_ERR;
	already_msg.length = strlen(ALREADY_ERR);
	already_msg.type = MSG_NO;

	EC_M1(signal_handling());


	/*Creazione code per richieste ed offerte*/
	offers = init_queue(offers, &offers_mtx);
	requests = init_queue(requests, &requests_mtx);

	/*Caricamento mappa e creazione file di log*/
	EC_NULL_M1(fdnodes = fopen(cities_filename, "r"));
	EC_NULL_M1(fdarcs = fopen(roads_filename, "r"));
	map = load_graph(fdnodes, fdarcs);
	if(map == NULL){
		printf("caricamento grafo fallito\n");
		return(-1);
	}
	fclose(fdnodes);
	fclose(fdarcs);
	EC_NULL_M1(logfile = fopen(LOG,"w"));

	/*Creazione dir tmp*/
	if (mkdir(TMP, O_CREAT | 0666) == -1 && errno != EEXIST) {
		return(-1);
	}

	/*Creazione lista utenti, condivisa dai thread worker*/
	user_list = malloc(sizeof(user_list_node));
	user_list->next = NULL;
	user_list->user = NULL;

	/*Apertura socket*/
	(void) unlink(SOCKPATH);
	sock_fd = 0;
	sock_fd = createServerChannel(SOCKPATH);
	EC_M1(sock_fd);

	/*Avvio del thread match*/
	if (pthread_create(&match_pt, NULL, match_start, NULL) != 0) {
		printf("errore create match\n");
		return(-1);
	}

	return (0);
}
/**
 *
 */
void serve() {
	int client_connection;
	pthread_t *new_worker;


	/*Ciclo di accettazione delle connessioni*/
	printf("SERVER: prima ciclo serve\n");
	while (pending_signal == 0) {
		printf("SERVER: inizio ciclo di serve\n");
		client_connection = acceptConnection(sock_fd);
		if (client_connection == -1) {
			//printf("SERVER: ERRORE IN CONNESSIONE\n");
			if (errno == EINTR) {
				//printf("SERVER: ERRNO EINTR pending %d\n",pending_signal);
				continue;
			}
			//else?
			/*Gestione errore*/
		}

		printf("SERVER: CONNESSIONE CLIENT\n");
		/*Ricevuta una richiesta di connessione si attiva uno worker per gestirla*/
		new_worker = malloc(sizeof(pthread_t));
//		if(new_worker == NULL){
//			return;
//		}
		if (pthread_create(new_worker, NULL, worker_start, &client_connection)
				!= 0) {
			//funz di uscita!
		}
		/*Attesa terminazione di eventuali thread ancora in esecuzione*/
		printf("SERVER: fine CICLO serve pending = %d\n",pending_signal);
	}
	printf("SERVER: fine serve!!!\n");
	return;

}

/**
 *
 * @return
 */
int signal_handling(void) {
	sigset_t set;
	struct sigaction *sa;
	//perchè non dichiararla invece di allocarla?

	/* Si mascherano tutti i segnali prima di installare gli opportuni gestori */
	EC_NULL_M1(sa = calloc(1,sizeof(struct sigaction))); //perchè calloc?
	EC_M1(sigfillset(&set))
	EC_M1(pthread_sigmask(SIG_SETMASK,&set,NULL))
	memset(&sa, sizeof(sa), 0);
	sa->sa_handler = SIG_IGN;
	//EC_M1(sigaction(SIGPIPE, sa, NULL));
	EC_M1(sigaction(SIGQUIT, sa, NULL));

	/* Gestione dei segnali */
	sa->sa_handler = sigterm_handler;
	EC_M1(sigaction(SIGTERM, sa, NULL));
	sa->sa_handler = sigint_handler;
	EC_M1(sigaction(SIGINT, sa, NULL));
	//nuovo testare bene
	sa->sa_handler = sigpipe_handler;
	EC_M1(sigaction(SIGPIPE, sa, NULL));


	/* Rimozione della maschera */
	EC_M1(sigemptyset(&set));
	EC_M1(pthread_sigmask(SIG_SETMASK, &set, NULL));
	free(sa);
	return(0);
}

/**
 *
 *
 * @param signum
 */
static void sigint_handler(int signum) {
	pending_signal = 1;
	printf("SIGINT MAIN!\n");
}

/**
 *
 * @param signum
 */
static void sigterm_handler(int signum) {
	pending_signal = 1;
	printf("SIGTERM MAIN!\n");
}

/**
 *
 * @param signum
 */
static void sigpipe_handler(int signum) {
	printf("SIGPIPE MAIN!\n");//capire come fare!!!!!!!

}

